#include <boost/bind.hpp>
#include "Server.h"
#include "Trace.h"
#include "MessageQueue.h"

using namespace std;

namespace tsc
{
namespace common
{

unsigned int Server::sInstanceCounter = 0x00001000;

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
Server::Server(const std::string& address,
               const std::string& port,
               boost::asio::io_service& service)
  : mService(service),
    mSignals(service),
    mAcceptor(service),
    mRequestHandler(),
    mInstance(++sInstanceCounter)
{
  ENTER("Server::Server");

  mSignals.add(SIGINT);
  mSignals.add(SIGTERM);
#if defined(SIGQUIT)
  mSignals.add(SIGQUIT);
#endif // defined(SIGQUIT)
  mSignals.async_wait(boost::bind(&Server::handleStop, this));

  // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
  boost::asio::ip::tcp::resolver        resolver(mService);
  boost::asio::ip::tcp::resolver::query query(address, port);
  mEndpoint = *resolver.resolve(query);

  mAcceptor.open(mEndpoint.protocol());
  mAcceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
  mAcceptor.bind(mEndpoint);
  mAcceptor.listen();

  // register Handler in message queue
  MessageQueue::instance()->registerServerHandler(&mRequestHandler);

  mDescribeStr = STR("Server instanceId=0x%08X.",
                     sInstanceCounter);

  TRACE(3, STR("%s Started at address=%s:%u.",
               mDescribeStr.data(),
               mEndpoint.address().to_string().data(),
               mEndpoint.port()));
  startAccept();

  RETURN;
} // Server::Server

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Server::startAccept()
{
  ENTER("Server::startAccept");

  ConnectionP_t connection(new Connection(mService, mRequestHandler));
  mConnections.push_back(connection);

  TRACE(3, STR("%s Accepting connection from client.",
               mDescribeStr.data()));
  mAcceptor.async_accept(connection.get()->socket(),
      boost::bind(&Server::handleAccept,
                  this,
                  boost::asio::placeholders::error,
                  connection.get()));

  RETURN;
} // Server::startAccept

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Server::handleAccept(const boost::system::error_code& error,
                          Connection*                      connection)
{
  ENTER("Server::handleAccept");

  if (!error)
  {
    TRACE(3, STR("%s Connection with client %s:%u established.",
                 mDescribeStr.data(),
                 connection->socket().remote_endpoint().address().to_string().data(),
                 connection->socket().remote_endpoint().port()));

    connection->start();
  }
  else
  {
    TRACE_ERROR(STR("%s Connection with client %s:%u failed. Error message '%s'.",
                mDescribeStr.data(),
                connection->socket().remote_endpoint().address().to_string().data(),
                connection->socket().remote_endpoint().port(),
                error.message().data()));
  }

  startAccept();

  RETURN;
} // Server::handleAccept

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Server::handleStop()
{
  ENTER("Server::handleStop");

  TRACE(3, STR("%s Stopped.",
               mDescribeStr.data()));
  mService.stop();

  RETURN;
}

} // namespace server2
} // namespace http
